package com.ruoyi.boat.controller;

import com.ruoyi.boat.codec.BdCodec;
import com.ruoyi.boat.codec.MessageEncode;
import com.ruoyi.boat.domain.Boat;
import com.ruoyi.boat.domain.Path;
import com.ruoyi.boat.domain.PathPoint;
import com.ruoyi.boat.domain.Status;
import com.ruoyi.boat.dto.CmdType;
import com.ruoyi.boat.dto.CommandDto;
import com.ruoyi.boat.service.IBoatService;
import com.ruoyi.boat.service.IPathPointService;
import com.ruoyi.boat.service.IPathService;
import com.ruoyi.boat.service.IStatusService;
import com.ruoyi.boat.tcp.cache.CommunicationType;
import com.ruoyi.boat.tcp.cache.WaveBoat;
import com.ruoyi.boat.tcp.cache.MyApplicationCache;
import com.ruoyi.boat.tcp.client.NettyClient;
import com.ruoyi.boat.tcp.client.TcpClientStarter;
import com.ruoyi.boat.tcp.server.RouteStatusEnum;
import com.ruoyi.boat.tcp.server.WaveBoatServer;
import com.ruoyi.boat.utils.ByteUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import io.netty.buffer.Unpooled;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Controller
@RequestMapping("/cmd")
@Slf4j
public class CommandController {
    @Autowired
    private IBoatService boatService;
    @Autowired
    private IPathService pathService;
    @Autowired
    private IPathPointService pathPointService;
    @Autowired
    private TcpClientStarter tcpClientStarter;
    @Autowired
    private WaveBoatServer boatServer;
    @Value("${netty.client.bd2No}")
    private String localBdNo;
    @Autowired
    private MyApplicationCache myApplicationCache;
    @Autowired
    private IStatusService statusService;

    @RequestMapping("/index/{boatId}")
    public String index(ModelMap mmap, @PathVariable Long boatId) {
        Boat boat = boatService.selectBoatById(boatId);
        mmap.put("boat", boat);
        mmap.put("status",statusService.selectLatestStatusByCode(boat.getCode()));
        return "boat/command/command";
    }

    @RequiresPermissions("boat:boat:cmd")
    @PostMapping("/ctrl")
    @ResponseBody
    public AjaxResult control(@RequestBody CommandDto commandDto) {
        log.info("control code: {}, key: {}, value: {}", commandDto.getBoatCode(), commandDto.getCmdCode(), commandDto.getValue());
        String boatCode = commandDto.getBoatCode();
        WaveBoat boat = myApplicationCache.getWaveBoat(boatCode);
        if (boat == null) {
            return AjaxResult.error("没有编号为" + boatCode + "的艇，请确认！");
        }
        String boatBdNo = boat.getBoatInfo().getCard();
        if (!StringUtils.isNotEmpty(boatBdNo)) {
            return AjaxResult.error("艇的北斗号未标注，请确认！");
        }
        CmdType cmdType = CmdType.getByCode(commandDto.getCmdCode());
        if (cmdType == null) {
            return AjaxResult.error("指令编号有误");
        }
        //1.创建指令
        CmdCreateRes res = createCmd(cmdType, commandDto.getValue(), boatCode);
        if (!res.ack) {
            return AjaxResult.error(res.msg);
        }
        //2.发送指令
        byte[] cmd = res.getCmd();
        log.info("cmd : " + ByteUtil.bytesToHex(cmd, " "));
        byte[] bdCmd = BdCodec.encodeBd4Dot0SendMsg(cmd, localBdNo, boatBdNo);
        log.info("bdCmd : " + ByteUtil.bytesToHex(bdCmd, " "));
        AjaxResult result = myApplicationCache.sendCmd(bdCmd);
        //3.后续处理并返回结果
        /* 平台需要回执
        if (result.isSuccess() && cmdType.equals(CmdType.PATH_PLANING)) {
            //更新航线信息
            if (res.getPath().getStatus() == RouteStatusEnum.newRoute.getStatus()) {
                //如果有正在运行的航迹 需要设置为取消状态
                Path activePath = pathService.selectActivePath(boatCode);
                if (activePath != null && activePath.getStatus() == RouteStatusEnum.navRoute.getStatus()
                        && activePath.getEndTime() == null) {
                    activePath.setStatus(RouteStatusEnum.cancelRoute.getStatus());
                    pathService.updatePath(activePath);
                }
                res.getPath().setStatus(RouteStatusEnum.navRoute.getStatus());
                res.getPath().setBeginTime(new Date());
                pathService.updatePath(res.getPath());
                pathPointService.setPathPointBeSentByIds(
                        res.getBeSendPoints().stream().map(item -> String.valueOf(item.getId()))
                                .collect(Collectors.joining(","))
                );
                boat.setCurrentPath(res.getPath());
            } else if (res.getPath().getStatus() == RouteStatusEnum.navRoute.getStatus()) {
                //更新航迹
                pathPointService.setPathPointBeSentByIds(
                        res.getBeSendPoints().stream().map(item -> String.valueOf(item.getId()))
                                .collect(Collectors.joining(","))
                );
                boat.setCurrentPath(res.getPath());
            }
        }
        */
        if(result.isSuccess()){
            myApplicationCache.setCmdRequest(boatCode,cmdType,MessageEncode.getLastCmdSerialNum(),cmdType.equals(CmdType.PATH_PLANING)?res:null);
        }
        return result;
    }

    /**
     * type == 0 新建 type == 1 增加点位
     */
    private List<byte[]> getRouteCmdList(List<PathPoint> list, int type, int existPointSize) {
        List<byte[]> cmdList = new ArrayList<>();
        int len = list.size() % 4 == 0 ? list.size() / 4 : list.size() / 4 + 1;
        for (int i = 0; i < len; i++) {
            int endIndex = Math.min(list.size(), (i + 1) * 4);
            if (type == 0 && i == 0) {
                cmdList.add(MessageEncode.genCreatePathCmd(list.subList(0, endIndex)));
            } else {
                cmdList.add(MessageEncode.genAppendPathCmd(list.subList(i * 4, endIndex), i * 4 + existPointSize));
            }
        }
        return cmdList;
    }

    @Data
    @Accessors(chain = true)
    public class CmdCreateRes {
        private byte[] cmd;
        private boolean ack;
        private String msg;
        private List<PathPoint> beSendPoints;
        private Path path;
    }

    //创建命令
    private CmdCreateRes createCmd(CmdType cmdType, Integer cmdValue, String boatCode) {
        CmdCreateRes res = new CmdCreateRes();
        byte[] cmd = null;
        String msg = null;
        switch (cmdType) {
            case SWITCH_MODE:
                cmd = MessageEncode.genCommunicationTypeCmd(cmdValue);
                break;
            case CHANGE_BEIDOU_ADDRESS:
                cmd = MessageEncode.genChangeBdAddressCmd(String.valueOf(cmdValue));
                break;
            case SWITCH_SEND_MODE:
                // todo 实现北斗切换下发指令
                msg = "切换北斗功能未实现，请等待后续版本！";
                break;
            case SET_RADIUS:
                cmd = MessageEncode.genChangeAllowableDeviationRadiusCmd(cmdValue);
                break;
            case SET_POINT:
                //todo 实现设置点位的功能
                msg = "设置点位功能未实现，请等待后续版本！";
                break;
            case PATH_PLANING: {
                Path path = pathService.selectPathById(cmdValue);
                List<PathPoint> unSendList = new ArrayList<>();
                List<PathPoint> sendedList = new ArrayList<>();
                path.getPathPointList().forEach(item -> {
                    if (!item.isSend()) {
                        unSendList.add(item);
                    } else {
                        sendedList.add(item);
                    }
                });
                if (unSendList.size() > 0) {
                    int endIndex = Math.min(4, unSendList.size());
                    List<PathPoint> pointsBeSend = unSendList.subList(0, endIndex);
                    if (Objects.equals(path.getStatus(), RouteStatusEnum.newRoute.getStatus())) {
                        //航迹下发一次最多不能超过4个点
                        cmd = MessageEncode.genCreatePathCmd(pointsBeSend);
                        res.setBeSendPoints(pointsBeSend).setPath(path);
                    } else if (Objects.equals(path.getStatus(), RouteStatusEnum.navRoute.getStatus())) {
                        //已经发送了航迹 现在需要新增加点位
                        cmd = MessageEncode.genAppendPathCmd(pointsBeSend, sendedList.size());
                        res.setBeSendPoints(pointsBeSend).setPath(path);
                    } else {
                        msg = "当前状态无法下发航迹点，请重新编辑！";
                    }
                } else {
                    msg = "没有航迹点待发送，请重新设置！";
                }
                break;
            }
            case DATA_DESTRUCTION:
                cmd = MessageEncode.genDataSelfDestroyCmd(cmdValue);
                break;
            case ROTATING_SPEED:
                //msg = "电机转速设置功能未实现，请等待后续版本！";
                cmd = MessageEncode.genRotationCmd(cmdValue);
                break;
            case SET_RUDDER_ANGLE:
                if (cmdValue == 0x00 || cmdValue == 0x1e || cmdValue == (0x82 & 0xff)) {
                    cmd = MessageEncode.genControlRudderCmd((byte) (cmdValue & 0xff));
                } else {
                    msg = "无效的舵角值";
                }
                break;
            case LOAD_POWER:
                /*Status status = myApplicationCache.getWaveBoat(boatCode).getStatus();
                if (status == null) {
                    msg = "未获得当前其余开关的状态，无法发送";
                } else {
                    int loadStatus = status.getLoadStatus();
                    int switchValue;
                    if (cmdValue >= 1000) {
                        //开
                        int temp = 0x01 << (cmdValue - 1000);
                        switchValue = (loadStatus & 0xff) | (temp & 0xff);
                    } else {
                        //关
                        int temp = 0xff - (0x01 << cmdValue);
                        switchValue = (loadStatus & 0xff) & (temp & 0xff);
                    }*/
//                    int switchValue = cmdValue;
                    cmd = MessageEncode.genLoadCmd(cmdValue);
                //}
                break;
            default:
                break;
        }
        if (cmd == null) {
            res.setMsg(msg);
        } else {
            res.setAck(true).setCmd(cmd);
        }
        return res;
    }
}
